package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"golang-socket.io-server/tools"
)

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method               // 请求方法
		origin := c.Request.Header.Get("Origin") // 请求头部
		var headerKeys []string                  // 声明请求头keys
		for k, _ := range c.Request.Header {
			headerKeys = append(headerKeys, k)
		}
		headerStr := strings.Join(headerKeys, ", ")
		if headerStr != "" {
			headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
		} else {
			headerStr = "access-control-allow-origin, access-control-allow-headers"
		}
		if origin != "" {
			c.Header("Access-Control-Allow-Origin", origin)                                    // 这是允许访问所有域
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") // 服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
			//  header的类型
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
			//              允许跨域设置                                                                                                      可以返回其他子段
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
			c.Header("Access-Control-Max-Age", "172800")                                                                                                                                                           // 缓存请求信息 单位为秒
			c.Header("Access-Control-Allow-Credentials", "true")                                                                                                                                                   //  跨域请求是否需要带cookie信息 默认设置为true
			c.Set("content-type", "application/json")                                                                                                                                                              // 设置返回格式是json
		}

		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.JSON(http.StatusOK, "Options Request!")
		}
		// 处理请求
		c.Next() //  处理请求
	}
}

func main() {
	// 创建 SocketIO 服务
	socketioServer := tools.NewSocketioServer()

	// 监听聊天消息事件
	socketioServer.OnChatMessage("/", "receiveChatMessage", "sendToUidDemo")

	// 启动 SocketIO 服务
	go socketioServer.Server.Serve()
	defer socketioServer.Server.Close()

	go func() {
		// 处理 `http` 请求
		http.Handle("/socket.io/", socketioServer.Server)
		// 静态文件服务
		http.Handle("/", http.FileServer(http.Dir("./asset")))
		log.Println(tools.ServerName + " Serving at localhost:8000...")
		log.Fatal(http.ListenAndServe(tools.ServerHost+":"+tools.ServerPort, nil))
	}()

	// 创建 Http 服务
	router := gin.New()
	router.POST("/demo", func(ctx *gin.Context) {
		fmt.Println("demo")
		// 向房间内的所有人员发消息
		// server.BroadcastToRoom("/", "test", "notice", "广播通知")

		fromUid := ctx.DefaultPostForm("from_uid", "")
		toUid := ctx.DefaultPostForm("to_uid", "")
		eventType := ctx.DefaultPostForm("event_type", "")
		messageType := ctx.DefaultPostForm("type", "1")
		content := ctx.DefaultPostForm("content", "")
		imageUrl := ctx.DefaultPostForm("image_url", "")
		isClose := ctx.DefaultPostForm("is_close", "0")
		if toUid == "" {
			ctx.JSON(200, gin.H{
				"status_code": 400,
				"msg":         "接受者Id错误",
				"data":        nil,
			})

			return
		}

		// 向特定客户端发送消息,方式一: 当客户端来连接时将当前客户的ID加入到房间，即每个客户一个房间
		socketioServer.Server.BroadcastToRoom("/", toUid, "receiveChatMessage", tools.Success(map[string]interface{}{
			"from_uid":    fromUid,
			"to_uid":      toUid,
			"event_type":  eventType,
			"type":        messageType,
			"content":     content,
			"image_url":   imageUrl,
			"is_close":    isClose,
			"is_read":     0,
			"status":      1,
			"create_time": time.Now().Format("2006-01-02 15:04:05"),
		}))

		// 向特定客户端发送消息,方式二: 存储客户连接
		// mu.Lock()
		// client, ok := clients[toUid]
		// mu.Unlock()

		// if !ok {
		// 	log.Println("client not found")
		// 	return
		// }
		// // 向特定客户端发送消息
		// client.Emit("message", map[string]string{"uid": "9999", "content": "xxxxxxx"})

		ctx.JSON(200, gin.H{
			"status_code": 200,
			"msg":         "发送成功",
			"data":        nil,
		})
	})

	// 测试 golang 客户端发送消息
	router.POST("/clientSendChatMessage", func(ctx *gin.Context) {
		fromUid := ctx.DefaultPostForm("from_uid", "0")
		toUid := ctx.DefaultPostForm("to_uid", "0")
		eventType := ctx.DefaultPostForm("event_type", "")
		messageType := ctx.DefaultPostForm("type", "1")
		content := ctx.DefaultPostForm("content", "")
		imageUrl := ctx.DefaultPostForm("image_url", "")
		isClose := ctx.DefaultPostForm("is_close", "0")

		// 使用zhouhui8915/go-socket.io-client
		// socketioClient, err := tools.NewSocketioClient(1)
		// if err != nil {
		// 	ctx.JSON(200, gin.H{
		// 		"status_code": 400,
		// 		"msg":         err.Error(),
		// 		"data":        nil,
		// 	})

		// 	return
		// }

		// 使用graarh/golang-socketio
		socketioClient, err := tools.NewSocketioClient(2)
		if err != nil {
			ctx.JSON(200, gin.H{
				"status_code": 400,
				"msg":         err.Error(),
				"data":        nil,
			})

			return
		}

		err = socketioClient.GosocketioClient.Emit("sendToUidDemo", map[string]interface{}{
			"from_uid":    fromUid,
			"to_uid":      toUid,
			"event_type":  eventType,
			"type":        messageType,
			"content":     content,
			"image_url":   imageUrl,
			"is_close":    isClose,
			"is_read":     0,
			"status":      1,
			"create_time": time.Now().Format("2006-01-02 15:04:05"),
		})
		if err != nil {
			ctx.JSON(200, gin.H{
				"status_code": 400,
				"msg":         err.Error(),
				"data":        nil,
			})

			return
		}

		// TODO关闭连接,有问题所有通过传递标识到服务端关闭连接
		// socketioClient.GosocketioClient.Close()

		ctx.JSON(200, gin.H{
			"status_code": 200,
			"msg":         "发送成功",
			"data":        nil,
		})
	})

	router.Use(gin.Recovery(), Cors())
	router.GET("/socket.io/*any", gin.WrapH(socketioServer.Server))
	router.POST("/socket.io/*any", gin.WrapH(socketioServer.Server))

	// 启动 Http 服务
	if err := router.Run(tools.HttpServerHost + ":" + tools.HttpServerPort); err != nil {
		log.Fatal("failed run app: ", err)
	}
}
